In dieser Lerneinheit werden wir uns mit dem Vergleich zwischen der ER-Modellierung und der UML-Klassenmodellierung beschäftigen. Beide Konzepte sind grundlegende Modellierungstechniken in der Informatik, die zwar unterschiedlichen Ursprungs sind, aber dennoch viele Gemeinsamkeiten aufweisen. Wir werden uns ansehen, wie diese beiden Konzepte zusammenhängen und wie man zwischen ihnen übersetzen kann. Beginnen wir mit der Frage, warum wir überhaupt einen Bezug zwischen ER und UML herstellen. Die ER-Modellierung existiert bereits seit neunzehnhundertsechsundsiebzig, als sie von Peter Chen veröffentlicht wurde. Seitdem hatte sie sich insbesondere im Zusammenhang mit der imperativen Programmierung etabliert. Vielleicht haben Sie schon vom EVA-Konzept gehört – Eingabe, Verarbeitung, Ausgabe – das eng mit der Datenverarbeitung zusammenhängt. Die relationalen Datenbankmanagementsysteme, kurz RDBMS, die mit der ER-Modellierung in Verbindung stehen, sind sehr performant und heutzutage sehr weit verbreitet. Zu den bekanntesten Beispielen zählen MySQL oder MariaDB, Microsoft SQL und Oracle Datenbank. Die UML hingegen hat sich später etabliert, als sich komplexere, objektorientierte Konzepte durchgesetzt hatten. Interessanterweise verbergen sich unter objektorientierten Anwendungen normalerweise auch heutzutage noch relationale Datenbanken. Es gibt zwar auch Objektdatenbankmanagementsysteme, abgekürzt ODBMS. Damit in Verbindung wurden die Object Query Language, OQL, sowie die Object Definition Language, ODL, als Datenmanipulationssprache definiert. Allerdings ist die Performance solcher Systeme noch problematisch. Bekannte Beispiele für ODBMS sind db-four-o sowie Zope. Wenn wir uns die Unterschiede zwischen ODBMS und RDBMS ansehen, wird deutlich, dass bei ODBMS eine direkte Abbildung von Objekten in die Datenbank erfolgt, während bei RDBMS eine objekt-relationale Abbildung notwendig ist. Diese Abbildung stellt einen Übersetzungsschritt dar, der zusätzlichen Aufwand verursacht, jedoch oft in Kauf genommen wird, da die Leistungsfähigkeit relationaler Datenbanken diesen Nachteil kompensiert. Mit der Objektorientierung hat sich der gesamte Software-Entwicklungsprozess verlagert. Früher stand die „Denkweise“ der Maschine im Vordergrund – Stichwort: Datenverarbeitung. Heute modelliert man einen Ausschnitt aus der realen Welt, wie Sie vielleicht aus der Veranstaltung Software-Engineering eins kennen. Diese Modellierung erfolgt heutzutage unter Verwendung von verschiedenen UML-Diagrammtypen. Dazu gehören Use Cases für die Anwendungsfälle beziehungsweise für die Funktionalitäten, die ein Benutzer in seiner Rolle gerne hätte. Aktivitätsdiagramme werden zur Modellierung von geschäftlichen Abläufen für die Fachlogik eingesetzt. Und Klassendiagramme dienen zur Darstellung von Eigenschaften, statt der ER-Modellierung, und Funktionen oder Methoden von Objekten für die Fachlogik. Bevor wir zur nächsten Ebene übergehen, denken Sie daran, wie wichtig es ist, die statische und dynamische Sicht im Zusammenspiel zu betrachten. Während die Aktivitätsdiagramme die dynamische Sicht auf das System darstellen, repräsentieren die Klassendiagramme die statische Sicht auf die Klassen und deren Beziehungen zueinander. Mögliche Beziehungen sind die Assoziation als „kennt“-Beziehung, die Aggregation oder Komposition als „besteht-aus“-Beziehung und die Vererbung als „ist-ein“-Beziehung. Wichtig zu verstehen ist, dass die Eigenschaften und die Beziehungen aus UML-Klassendiagrammen direkt in ER-Diagramme überführbar sind und umgekehrt. Sie fragen sich vielleicht, warum die Funktionen beziehungsweise Methoden der UML-Klassendiagramme nicht überführbar sind? Das liegt daran, dass relationale Datenbanken auf das Speichern von Daten ausgerichtet sind, nicht auf das Speichern von Verhalten oder Programmlogik. Betrachten wir nun eine einzelne Klasse. Eine UML-Klasse entspricht einer relationalen Tabelle, einer Entitätsklasse. Eine Eigenschaft beziehungsweise ein Attribut einer UML-Klasse in Form eines primitiven Typs oder eines Strings entspricht einem Attribut beziehungsweise einer Spalte der Tabelle. Die Datentypen der Programmiersprache sind dabei in SQL-Datentypen zu überführen. Eine Eigenschaft beziehungsweise ein Attribut einer UML-Klasse in Form einer Referenz wird im relationalen Datenmodell in der Regel als Fremdschlüssel realisiert. Eine Tabellenzeile in einer relationalen Datenbank ist ein Datensatz. Ein Datensatz entspricht in der Regel genau einem Objekt dieser Klasse. Ein weiteres Beispiel für mehrwertige Attribute wäre eine Person mit mehreren Telefonnummern oder E-Mail-Adressen. Mehrwertige Attribute stellen eine besondere Herausforderung dar. Mehrwertige Attribute werden im relationalen Modell durch eine neue Tabelle dargestellt. Nehmen wir als Beispiel eine Zahlungsart, die mehrere Werte annehmen kann, wie bar, EC, Visa, Master oder PayPal. Ein Entitätstyp Rechnung könnte eine bestimmte Zahlungsart referenzieren. Wir brauchen eine separate Tabelle für die Zahlungsarten und einen Fremdschlüssel in der Rechnungstabelle, der auf einen Eintrag in der Zahlungsart-Tabelle verweist. Alternativ kann man in Programmiersprachen wie Java und Datenbanksystemen wie MySQL oder MariaDB einen eigenen Datentyp „Enum“ verwenden! Dies vereinfacht die Modellierung, da kein Fremdschlüssel benötigt wird, sondern direkt ein Wert aus einer vordefinierten Menge von Werten verwendet werden kann. Stellen Sie sich vor, wir haben eine Rechnung mit einer Zahlungsart als Enum-Attribut und einen Rechnungsstatus ebenfalls als Enum. Diese können direkt als Attribute der Rechnung modelliert werden, ohne zusätzliche Tabellen zu benötigen. Wenn wir uns den Unterschied zwischen einem Objekt und einer Datenbank-Tabelle ansehen, wird deutlich, dass ein Objekt sowohl Daten als auch Methoden enthält. Die Daten sind die Eigenschaften des Objekts, die Methoden beschreiben sein Verhalten. In einer Datenbank-Tabelle können wir jedoch nur die Daten speichern, nicht das Verhalten. Betrachten wir ein Beispiel: Eine Rechnung hat Attribute wie Rechnungsnummer, Datum und Zahlungsart sowie Methoden wie get-Datum, drucken und set-Status. In der Datenbank können wir nur die Attribute speichern, die Methoden müssen in der Anwendung implementiert werden. Wenn wir die Daten eines Objekts in einer Datenbank-Tabelle speichern, können wir Referenzen auf andere Objekte als Fremdschlüssel darstellen. Zum Beispiel könnte die Rechnungstabelle einen Fremdschlüssel enthalten, der auf die Zahlungsart-Tabelle verweist. Im Objekt selbst wäre dies eine direkte Referenz auf ein Zahlungsart-Objekt. Kommen wir nun zur Umsetzung der Assoziation, der „kennt“-Beziehung in einer eins-zu-n-Beziehung. Im Beispiel ist auch bereits eine Assoziation enthalten: Die Rechnung kennt ihre Zahlungsart. Jede konkrete Zahlungsart ist ein Objekt der Klasse Zahlungsart. Aus Sicht des relationalen Modells wird diese Referenz über einen Primärschlüssel und einen Fremdschlüssel realisiert. Aus Sicht des relationalen Modells wird diese Referenz über einen Primärschlüssel und einen Fremdschlüssel realisiert. Was würde passieren, wenn man diese Rechnung löscht? Die Zahlungsart würde weiterbestehen, da andere Rechnungen sie möglicherweise auch verwenden. Was würde passieren, wenn man diese Zahlungsart löscht? Dies könnte zu inkonsistenten Daten führen, da Rechnungen auf eine nicht mehr existierende Zahlungsart verweisen würden. Daher müssen wir beim Datenbankdesign Regeln für die referentielle Integrität festlegen. Betrachten wir nun die Umsetzung der Assoziation in einer eins-zu-eins-Beziehung. Als Beispiel könnte ein Kunde Kreditkarteninformationen haben. Ein Kunde kann höchstens eine Kreditkarte haben, und eine Kreditkarte gehört genau zu einem Kunden. Wie setzen wir das in Tabellen um? Wir haben eine Kundentabelle mit einer Kundennummer als Primärschlüssel und einem optionalen Fremdschlüssel auf die Kreditkarteninformationen. Die Kreditkarteninformationen haben eine ID als Primärschlüssel und einen Fremdschlüssel auf den Kunden. Beide Fremdschlüssel müssen als „unique“ definiert sein, um die eins-zu-eins-Beziehung sicherzustellen. Eine etwas komplexere eins-zu-eins-Beziehung entsteht, wenn mehrere Entitäten dasselbe referenzieren. Nehmen wir als Beispiel Lieferanten und Kunden, die beide Adressen haben. Der Eintrag, die Zeile, die Entität in den Tabellen Kunde und Lieferant haben jeweils eine Referenz auf eine Adress-ID. Wenn die Adresse auch den zugehörigen Lieferanten oder Kunden kennen soll, so müssen zwei separate Spalten id\_kunde und id\_lieferant für die Fremdschlüssel-Beziehung erstellt werden, die entweder-oder ausgefüllt werden. Eine passende Logik dazu ist gegebenenfalls nur über eine Stored Procedure realisierbar! Kommen wir zur Umsetzung der Assoziation in einer n-zu-m-Beziehung. Ein Beispiel wäre ein Lieferant, der mehrere Produkte anbietet, und ein Produkt, das von mehreren Lieferanten geliefert werden kann. Zusätzlich gibt es Informationen über den Rabatt, den ein Lieferant für ein bestimmtes Produkt gewährt. In der UML stellen wir dies als Assoziationsklasse dar, im ER-Modell als eigene Entität mit Beziehungen zu beiden Hauptentitäten. Bei der Umsetzung in Tabellen benötigen wir drei Tabellen: eine für Lieferanten, eine für Produkte und eine für den ProduktRabatt, die Fremdschlüssel auf beide Haupttabellen enthält sowie die zusätzlichen Attribute wie Menge und Rabatt. Diese n-zu-m-Beziehung mit einer Zwischentabelle sollten wir noch genauer betrachten. Die Tabelle ProduktRabatt enthält Spalten für id\_lieferant und id\_produkt, die zusammen einen zusammengesetzten Primärschlüssel bilden können. Zusätzlich enthält sie Spalten für Menge und Rabatt in Prozent. Die Kombination aus Lieferant und Produkt muss eindeutig sein, kann aber mehrfach vorkommen, wenn verschiedene Mengen unterschiedliche Rabatte haben. In diesem Fall würden wir einen dritten Schlüsselbestandteil benötigen, zum Beispiel die Menge. Wenden wir uns nun der Umsetzung der Aggregation und Komposition zu, also der „besteht-aus“-Beziehung. Ein Ganzes besteht aus seinen Teilen. Können die Teile auch unabhängig existieren von einem Ganzen und gegebenenfalls zu mehreren Ganzen gehören, so spricht man von einer Aggregation. Kann das Teil nicht selbständig ohne sein Ganzes existieren, so spricht man von einer Komposition. Wie Sie eine Aggregation oder Komposition in Java umsetzen, kennen Sie aus der Veranstaltung „Programmierung-zwei“. Eine Aggregation oder Komposition sieht im UML-Klassendiagramm wie eine Assoziation mit einer Raute am Ende des Ganzen aus, wobei die Raute bei der Komposition ausgefüllt ist. Nehmen Sie folgende Aufgabenstellung an: Jedes Fahrrad besteht aus einem Rahmen und aus Rädern. Ein Rahmen besteht aus Rohren und aus einem Lenker. Räder bestehen aus Felgen und aus Speichen. In einem ER-Diagramm sieht dies prinzipiell wie eine Hierarchie von Entitäten aus, die durch „part-of“-Beziehungen verbunden sind. Die Entität Fahrräder steht oben, darunter befinden sich die Entitäten Rahmen und Räder, und noch eine Ebene tiefer die Entitäten Rohre, Lenker, Felgen und Speichen. Die Komposition wird in einem relationalen Datenmodell prinzipiell genauso gehandhabt wie eine Aggregation. Um die Integrität der Daten sicherzustellen, muss man dafür sorgen, dass alle Teile auch gelöscht werden, wenn das dazugehörige Ganze in der Datenbank gelöscht wird. Dies kann durch Fremdschlüsselbeziehungen mit CASCADE DELETE-Option realisiert werden. Bei einer Aggregation hingegen würden die Teile weiterbestehen, selbst wenn das Ganze gelöscht wird, da sie auch zu anderen Ganzen gehören können. Betrachten wir ein Beispiel für die Umsetzung der Komposition: Eine Rechnung besteht aus mehreren Rechnungspositionen. Eine Rechnungsposition kann nicht ohne die zugehörige Rechnung existieren. Im UML-Diagramm würden wir dies als Komposition darstellen, mit einer ausgefüllten Raute am Ende der Rechnung. Im relationalen Modell hätten wir eine Rechnungstabelle mit der Rechnungsnummer als Primärschlüssel und eine Rechnungspositionstabelle mit einem zusammengesetzten Primärschlüssel aus Rechnungsnummer und laufender Nummer. Die Rechnungsnummer in der Rechnungspositionstabelle ist gleichzeitig Fremdschlüssel auf die Rechnungstabelle. Wenn eine Rechnung gelöscht wird, sollten alle zugehörigen Rechnungspositionen automatisch mit gelöscht werden. Im E-R-Diagramm erkennen sie die abhängige schwache Entität der Rechnungsposition. Kommen wir nun zur Umsetzung der Vererbung, der „ist-ein“-Beziehung. „Ist-ein“-Beziehungen drücken Spezialisierungen beziehungsweise Generalisierungen aus, eine Vererbung der Objektorientierung! Dabei gilt, dass der Spezialentitätstyp alle Attribute des allgemeinen Entitätstyps erbt. Aus diesem Grund findet sich bei diesen Beziehungen vom Typ eins-zu-eins an dem speziellen Entitätstyp keine Schlüsselattributangabe. „Ist-ein“-Beziehungen lassen sich auflösen und in eine Tabelle integrieren. Hierzu muss nur der Primärschlüssel des allgemeineren Entitätstyps entweder als Fremdschlüssel, bei eins-zu-n, oder als Primärschlüssel, bei eins-zu-eins, in die Tabelle des speziellen Entitätstyps übernommen werden. Jede Vererbung in der Objektorientierung können Sie im ER-Diagramm als schwache Entität definieren, da eine Unterklasse ohne ihre Oberklasse nicht lebensfähig ist. Als Beispiel kann ein Kunde entweder ein Privatkunde oder ein Geschäftskunde sein. Im UML-Diagramm werden wir dies als Vererbungshierarchie darstellen, mit Kunde als Oberklasse und Privatkunde und Geschäftskunde als Unterklassen. Der VIP-Kunde ist nochmals ein spezieller Geschäftskunde, der einen Rabatt bekommt. Jeder Kunde hat eine Kundennummer und gegebenenfalls weitere Attribute, die zu jedem Kunden gehören. Nur ein Privatkunde hat Attribute wie Vorname und Nachname. Jeder Geschäftskunde hat hingegen eindeutige Attribute wie einen Firmennamen und eine Steuer-ID. Der VIP-Kunde ist wiederum ein spezieller Geschäftskunde mit einem Rabatt. Sauberer wäre es noch in der Implementierung, wenn der Geschäftskunde neben der Kundennummer eine laufende Nummer hätte, die beide zusammen einen Primärschlüssel bilden würden. Ein Geschäftskunde ist eine schwache Entität gegen den Kunden, kann allein nicht existieren. Dann könnte sich der VIP-Kunde direkt auf einen speziellen Geschäftskunden beziehen. So, wie es jetzt ist, müsste die Vererbungshierarchie teilweise durch die Fachlogik garantiert werden, was man eigentlich nicht möchte. Mit derselben Begründung müsste der VIP-Kunde dann auch seinerseits eine laufende Nummer hinzufügen und diese zusätzlich zur laufenden Nummer eines Geschäftskunden sowie der Kundennummer als Primärschlüssel nutzen. Ich denke, Sie erkennen so langsam den Overhead, der durch die Umsetzung von Vererbungshierarchien entsteht, wenn man diese sauber implementieren will.